<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PICTURE LANGUAGE</title>
</head>
<body>
    <canvas id="canvas" style="border: 1px solid;" width="500" height="500"></canvas>
<script>
/*
    make_*      --> 用于创建 高级过程（higher procedure)
    create_*    --> 用于方便快捷构建
    *_*         --> 过程的命名，所有对象的属性值 也是 过程取值      ex: point.x_cor()
    aA          --> 驼峰命名 用于非过程     
*/



// picture language
function right_split(painter, n) {
    if (n === 0) return painter;
    let nextPainter = right_split(painter, n - 1);
    let rightPainter = below(nextPainter, nextPainter);
    return beside(painter, rightPainter);
}
let right_split2 = split(beside, below);
let up_split2 = split(below, beside);

function up_split(painter, n) {
    if (n === 0) return painter;
    let nextPainter = up_split(painter, n - 1);
    let upPainter = beside(nextPainter, nextPainter);
    return below(painter, upPainter);
}

function square_of_four(op1, op2, op3, op4) {
    return function (p) {
        return below(beside(op3(p), op4(p)), beside(op1(p), op2(p)));
    };
}
function identity(v) {
    return v;
}

function square_painter(painter) {
    return square_of_four(flip_horiz, identity,
                        rotate180, flip_vertical)(painter);
}
function anti_square_painter(painter) {
    return square_of_four(flip_vertical, rotate180,
                        identity, flip_horiz)(painter);
}
function corner_split(painter, n) {
    if (n === 0) return painter;
    let up_painter = up_split(painter, n - 1);
    let right_painter = right_split(painter, n - 1);
    let corner_painter = corner_split(painter, n - 1);
    return beside(
        below(painter, beside(up_painter, up_painter)),
        below(below(right_painter, right_painter), corner_painter)
    );
}

function split(op1, op2) {
    function recurse(p, n) {
        if (n === 0) return p;
        let nextPainter = recurse(p, n - 1);
        return op1(p, op2(nextPainter, nextPainter));
    }
    return recurse;
}

// construct vector
class Vector {
    constructor (x, y) {
        this.x = x;
        this.y = y;
    }

    x_cor() {
        return this.x;
    }
    y_cor() {
        return this.y;
    }
}


function add_vect (vector1, vector2) {
    return new Vector(
        vector1.x_cor() + vector2.x_cor(),
        vector1.y_cor() + vector2.y_cor()
    );
}

function sub_vect (vector1, vector2) {
    return new Vector(
        vector1.x_cor() - vector2.x_cor(),
        vector1.y_cor() - vector2.y_cor()
    );
}

function scale_vect (scalar, vector) {
    return new Vector(
        scalar * vector.x_cor(),
        scalar * vector.y_cor()
    );
}

// frame
class Frame {
    constructor (origin, edge1, edge2) {
        this.data = [origin, edge1, edge2];
    }

    origin () {
        return this.data[0];
    }

    edge1 () {
        return this.data[1];
    }

    edge2 () {
        return this.data[2];
    }
}
function create_frame(oXcor, oYcor, e1Xcor, e1Ycor, e2Xcor, e2Ycor) {
    let origin = new Vector(oXcor, oYcor);
    let edge1 = new Vector(e1Xcor, e1Ycor);
    let edge2 = new Vector(e2Xcor, e2Ycor);
    return new Frame(origin, edge1, edge2);
}

function frame_coor_map (frame) {
    let origin = frame.origin();
    let edge1 = frame.edge1();
    let edge2 = frame.edge2();
    return function (unit) {
        return add_vect(origin,
                        add_vect(scale_vect(unit.x_cor(), edge1),
                                 scale_vect(unit.y_cor(), edge2)));
    };
}

// line segment
class Segment {
    constructor (start, end) {
        this.segment = [start, end];
    }

    start_point () {
        return this.segment[0];
    }
    end_point () {
        return this.segment[1];
    }
}

// painter
function segment_to_painter(segments) {
    return function (frame) {
        let to_frame_map = frame_coor_map(frame);
        segments.forEach(function (seg) {
            let startPoint = seg.start_point();
            let endPoint = seg.end_point();
            drawline(to_frame_map(startPoint), to_frame_map(endPoint));
        })
    };
}

function image_to_painter(img) {
    return function (frame) {
        
    };
}

function transform_painter(painter, origin, edge1, edge2) {
    return function (frame) {
        let vect_map = frame_coor_map(frame);
        let newOrigin = vect_map(origin);
        let transformedFrame = new Frame(newOrigin, sub_vect(vect_map(edge1), newOrigin), sub_vect(vect_map(edge2), newOrigin));
        painter(transformedFrame);
    };
}


function beside(painter1, painter2) {
    let leftOrigin = new Vector(0, 0);
    let leftEdge1 = new Vector(0.5, 0);
    let leftEdge2 = new Vector(0, 1);
    let rightOrigin = leftEdge1;
    let rightEdge1 = new Vector(1, 0);
    let rightEdge2 = new Vector(0.5, 1);
    let left_painter = transform_painter(painter1, leftOrigin, leftEdge1, leftEdge2);
    let right_painter = transform_painter(painter2, rightOrigin, rightEdge1, rightEdge2);
    return function (frame) {
        left_painter(frame);
        right_painter(frame);
    };
}
function below(painter1, painter2) {
    let p1Origin = new Vector(0, 0);
    let p1Edge1 = new Vector(1, 0);
    let p1Edge2 = new Vector(0, 0.5);
    let p2Origin = p1Edge2;
    let p2Edge1 = new Vector(1, 0.5);
    let p2Edge2 = new Vector(0, 1);
    let top_painter = transform_painter(painter1, p1Origin, p1Edge1, p1Edge2);
    let bottom_painter = transform_painter(painter2, p2Origin, p2Edge1, p2Edge2);
    return function (frame) {
        top_painter(frame);
        bottom_painter(frame);
    };
}
function below2(painter1, painter2) {
    let p1 = rotate270(painter1);
    let p2 = rotate270(painter2);
    return rotate90(beside(p1, p2));
}

function shrink_to_upper_right(painter) {
    let origin = new Vector(0.5, 0.5);
    let edge1 = new Vector(1, 0.5);
    let edge2 = new Vector(0.5, 1);
    return transform_painter(painter, origin, edge1, edge2);
}

function rotate90(painter) {
    let o = new Vector(1, 0);
    let e1 = new Vector(1, 1);
    let e2 = new Vector(0 , 0);
    return transform_painter(painter, o, e1, e2);
}

function squash_inwards(painter) {
    let o = new Vector(0, 0);
    let e1 = new Vector(0.65, 0.35);
    let e2 = new Vector(0.35, 0.65);
    return transform_painter(painter, o, e1, e2);
}

function flip_horiz(painter) {
    let o = new Vector(1, 0);
    let e1 = new Vector(0 , 0);
    let e2 = new Vector(1, 1);
    return transform_painter(painter, o, e1, e2);
}
function flip_vertical(painter) {
    let o = new Vector(0, 1);
    let e1 = new Vector(1 , 1);
    let e2 = new Vector(0, 0);
    return transform_painter(painter, o, e1, e2);
}

function rotate180(painter) {
    let o = new Vector(1, 1);
    let e1 = new Vector(0, 1);
    let e2 = new Vector(1, 0);
    return transform_painter(painter, o, e1, e2);
}

function rotate270(painter) {
    let o = new Vector(0, 1);
    let e1 = new Vector(0, 0);
    let e2 = new Vector(1, 1);
    return transform_painter(painter, o, e1, e2);
}

function create_segments(arr) {
    return arr.map(create_segment);
}
function create_segment(arr) {
    return new Segment(new Vector(arr[0], arr[1]), new Vector(arr[2], arr[3]));
}

let painter_A = segment_to_painter(create_segments([
    [0, 0, 0, 1],
    [0, 0, 1, 0],
    [1, 0, 1, 1],
    [0, 1, 1, 1]
]));

let painter_B = segment_to_painter(create_segments([
    [0, 0, 1, 1],
    [1, 0, 0, 1]
]));

let painter_C = segment_to_painter(create_segments([
    [0.5, 0, 0, 0.5],
    [0.5, 0, 1, 0.5],
    [0.5, 1, 0, 0.5],
    [0.5, 1, 1, 0.5]
]));




</script>
<script>
// canvas
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');

const origin = new Vector(0, 500);

function fit_origin(origin, point) {
    return new Vector(origin.x_cor() + point.x_cor(), origin.y_cor() - point.y_cor());
}

function make_drawline(ctx, origin) {
    return function (start, end) {
        let s = fit_origin(origin, start);
        let e = fit_origin(origin, end);
        ctx.beginPath();
        ctx.moveTo(s.x_cor(), s.y_cor());
        ctx.lineTo(e.x_cor(), e.y_cor());
        ctx.closePath();
        ctx.stroke();
    };
}



let drawline = make_drawline(ctx, origin);


// example
let p1 = new Vector(0, 0);
let p2 = new Vector(20, 20);

let f1 = create_frame(50, 50, 400, 0, 0, 400);
let painter_Wave = segment_to_painter(create_segments([
    [0.45, 1, 0.4, 0.82],
    [0.4, 0.82, 0.48, 0.65],
    [0.48, 0.65, 0.35, 0.65],
    [0.35, 0.65, 0.2, 0.55],
    [0.2, 0.55, 0, 0.65],
    [0, 0.55, 0.2, 0.45],
    [0.2, 0.45, 0.45, 0.6],
    [0.45, 0.6, 0.26, 0],
    [0.37, 0, 0.5, 0.35],
    [0.5, 0.35, 0.6, 0],
    [0.75, 0, 0.6, 0.55],
    [0.6, 0.55, 1, 0.3],
    [1, 0.42, 0.72, 0.62],
    [0.72, 0.62, 0.6, 0.6],
    [0.6, 0.6, 0.7, 0.82],
    [0.7, 0.82, 0.6, 1],
    [0.48, 0.8, 0.51, 0.8],
    [0.58, 0.8, 0.61, 0.8],
    [0.5, 0.7, 0.6, 0.7],
]));
// painter_Wave(f1);

// beside(painter_Wave, flip_horiz(painter_Wave))(f1);

let corner_painter = corner_split(painter_Wave, 4);
// anti_square_painter(corner_painter)(f1);
square_painter(corner_painter)(f1);
</script>
</body>
</html>